package upload;

import io.swagger.annotations.ApiOperation;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import upload.dao.DatabaseMapper;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

@RestController
public class UploadController {

    /**
     * 文件上传路径
     */
    @Value("${file.savePath}")
    String savePath;

    /**
     * 文件导入完成后的保存路径
     */
    @Value("${file.recyclePath}")
    String recyclePath;

    /**
     * 文件导入完成后的保存路径
     */
    @Value("${file.legalType}")
    String legalType;

    /**
     * python环境
     */
    @Value("${python.env}")
    String pythonEnv;

    /**
     * 导入shp格式的python文件
     */
    @Value("${python.import.shp.file}")
    String pythonImportShp;

    /**
     * 导入geotiff格式的python文件
     */
    @Value("${python.import.geotiff.file}")
    String pythonImportGeotiff;

    @Autowired
    DatabaseMapper databaseMapper;

    /**
     * 保存文件API
     * @param file 文件
     * @return 上传结果
     */
    @ApiOperation(value="保存文件", notes="保存文件",httpMethod = "POST")
    @RequestMapping(value = "/upload")
    @ResponseBody
    public String saveFile(@RequestParam("file") MultipartFile file) throws Exception{
        // 获取文件名
        String fileName = file.getOriginalFilename();
        assert fileName != null;
        // 校验文件格式是否正确
        if(!isLegal(fileName)) return "文件非法";
        // 校验数据库中表是否存在
        if (isExist(fileName)) return "表已存在";
        // 保存上传文件
        fileName = savePath+fileName;
        if (!save(file,fileName)) return "上传失败";
        // 解压上传文件
        String tagetDir = uZip(fileName);
        if (StringUtils.isBlank(tagetDir)) return "解压失败";
        // 校验文件类型获取文件名
        FileType type = getFileType(tagetDir);
        if (type==null) return "文件类型不满足";
        // 校验数据完整性
        if (!type.isComplete(tagetDir)) return "文件不完整";
        // 调用python，获取返回值，并确定导入结果
        if(!importDatabase(tagetDir,type)) return "上传失败";
        // 移动导入完成的文件
        if(!moveToRecycle(fileName,tagetDir)) return "回收失败";
        return "上传成功";
    }

    private boolean importDatabase(String tagetDir, FileType type) {
        String[] args;
        String result = "";
        if (type==FileType.SHP){
            args = new String[] { pythonEnv, pythonImportShp,tagetDir};
        }else if(type==FileType.GEOTIFF){
            args = new String[] { pythonEnv, pythonImportGeotiff,tagetDir};
        }else {
            return false;
        }
        try {
            Process proc = Runtime.getRuntime().exec(args);
            BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String line = null;
            while ((line = in.readLine()) != null) {
//                System.out.println(line);
                result = line;
            }
            in.close();
            proc.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if ("success".equals(result)){
            return true;
        }else {
            System.out.println(result);
            return false;
        }
    }

    private FileType getFileType(String tagetDir) {
        for (FileType value : FileType.values()) {
            if (value.is(tagetDir)) return value;
        }
        return null;
    }

    private boolean isLegal(String fileName) {
        /**
         * 获取后缀: .***
         */
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        String[] strArr = legalType.split(",");
        for (String s : strArr) {
            if(suffixName.substring(1).equals(s)) return true;
        }
        return false;
    }

    private boolean isExist(String fileName) {
        String tableName = fileName.substring(0,fileName.lastIndexOf("."));
        //返回值为t时，表示表已存在
        if(databaseMapper.tableIsExist(tableName).equals("t")) return true;
        return false;
    }

    private boolean save(MultipartFile file,String path){
        // 文件对象
        File dest = new File(path);
        // 创建路径
        if(!dest.getParentFile().exists()){
            dest.getParentFile().mkdir();
        }
        try {
            //保存文件
            file.transferTo(dest);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    private String uZip(String path){
        String uZipDir = "";
        // 创建目标文件夹
        File tempFile = new File(path.trim());
        String filename = tempFile.getName().substring(0,tempFile.getName().lastIndexOf("."));
        String targetPath = tempFile.getParent()+"\\"+filename;
        File targetFile = new File(targetPath);
        if (!targetFile.exists()){
            targetFile.mkdir();
        }
        // 解压文件
        try {
            ZipFile zipFile = new ZipFile(path.trim());
            zipFile.extractAll(targetPath);
            uZipDir = targetPath;
        } catch (ZipException e) {
            e.printStackTrace();
        }
        return uZipDir;
    }

    private boolean deleteDir(String path){
        File file = new File(path);
        if(!file.exists()){//判断是否待删除目录是否存在
            System.err.println("The dir are not exists!");
            return false;
        }

        String[] content = file.list();//取得当前目录下所有文件和文件夹
        for(String name : content){
            File temp = new File(path, name);
            if(temp.isDirectory()){//判断是否是目录
                deleteDir(temp.getAbsolutePath());//递归调用，删除目录里的内容
                temp.delete();//删除空目录
            }else{
                if(!temp.delete()){//直接删除文件
                    System.err.println("Failed to delete " + name);
                }
            }
        }
        file.delete();
        return true;
    }

    private boolean moveToRecycle(String fileName, String tagetDir) {
        try {
            // 校验回收站是否存在
            File recycle = new File(recyclePath);
            if (!recycle.exists()) recycle.mkdir();
            File tempFile = new File(fileName.trim());
            String targetPath = recyclePath+"/"+System.currentTimeMillis()+tempFile.getName();
            File targetFile = new File(targetPath);
            tempFile.renameTo(targetFile);
            deleteDir(tagetDir);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
}

enum FileType{
    SHP("SHP", path -> {
        boolean haveSHX = false;
        boolean haveDBF = false;
        File pathFile = new File(path);
        String[] strArr = pathFile.list();
        for (String s : strArr) {
            if (s.contains(".shx")) haveSHX=true;
            if (s.contains(".dbf")) haveDBF=true;
        }
        return haveSHX||haveDBF;
    },path -> {
        boolean haveSHP = false;
        File pathFile = new File(path);
        String[] strArr = pathFile.list();
        for (String s : strArr) {
            if (s.contains(".shp")) haveSHP=true;
        }
        return haveSHP;
    }),
    GEOTIFF("GEOTIFF",path -> {
        boolean haveTFW = false;
        File pathFile = new File(path);
        String[] strArr = pathFile.list();
        for (String s : strArr) {
            if (s.contains(".tfw")) haveTFW=true;
        }
        return haveTFW;
    },path -> {
        boolean haveTIF = false;
        File pathFile = new File(path);
        String[] strArr = pathFile.list();
        for (String s : strArr) {
            if (s.contains(".tif")) haveTIF=true;
        }
        return haveTIF;
    });
    String type;
    CheckComplete check;
    CheckType checkType;
    FileType(String type,CheckComplete check,CheckType checkType){
        this.type = type;
        this.check = check;
        this.checkType = checkType;
    }
    boolean isComplete(String path){
        return this.check.isComplete(path);
    }
    boolean is(String path){
        return this.checkType.is(path);
    }
    interface CheckComplete{
        public boolean isComplete(String path);
    }
    interface CheckType{
        public boolean is(String path);
    }
}